package bma.common.langutil.cron;

import java.util.Date;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicBoolean;

import bma.common.langutil.concurrent.ProcessTimerTaskAbstract;
import bma.common.langutil.concurrent.TimerManager;
import bma.common.langutil.core.DateTimeUtil;

public class CrontabService {

	final org.slf4j.Logger log = org.slf4j.LoggerFactory
			.getLogger(CrontabService.class);

	private ScheduledExecutorService service;
	private TimerManager timer;
	private boolean defaultService;

	public ScheduledExecutorService getService() {
		return service;
	}

	public void setService(ScheduledExecutorService service) {
		this.service = service;
	}

	public void setDefaultService(boolean v) {
		this.defaultService = true;
		this.service = Executors.newSingleThreadScheduledExecutor();
	}

	public void close() {
		if (this.defaultService) {
			this.service.shutdown();
		}
	}

	public TimerManager getTimer() {
		return timer;
	}

	public void setTimer(TimerManager timer) {
		this.timer = timer;
	}

	public CrontabTask createTask(final String name, final String cron,
			final Runnable runable) {
		final CrontabSchedule cs = CrontabParser.marshall(cron);
		return new CrontabTask() {

			AtomicBoolean started = new AtomicBoolean();

			@Override
			public void stop() {
				if (log.isInfoEnabled()) {
					log.info("'{}' stop()", name);
				}
				started.compareAndSet(true, false);
			}

			@Override
			public void start() {
				if (log.isInfoEnabled()) {
					log.info("'{}' start({})", name, cron);
				}
				if (!started.compareAndSet(false, true)) {
					return;
				}
				runNext();
			}

			protected void runNext() {
				long time = cs.nextInterval(new Date());
				runAt(System.currentTimeMillis() + time);
			}

			protected void execute() {
				if (log.isInfoEnabled()) {
					log.info("'{}' run", name);
				}
				try {
					if (!started.get()) {
						if (log.isInfoEnabled()) {
							log.info("'{}' stoped, skip execute", name);
						}
						return;
					}
					runable.run();
					if (!started.get()) {
						if (log.isDebugEnabled()) {
							log.debug("'{}' stoped, skip runNext", name);
						}
						return;
					}
					runNext();
				} catch (Exception err) {
					log.warn("'{}' run fail,stop", err);
				}
			}

			protected void runAt(long time) {
				if (log.isDebugEnabled()) {
					log.debug("'{}' plan {}", name,
							DateTimeUtil.formatDateTime(new Date(time)));
				}
				if (service != null) {
					service.schedule(new Runnable() {

						@Override
						public void run() {
							execute();
						}
					}, time - System.currentTimeMillis(), TimeUnit.MILLISECONDS);
				} else {
					TimerManager tm = TimerManager.checkInstance(timer);
					tm.postTimerTask(new ProcessTimerTaskAbstract(time) {

						@Override
						public void run() {
							execute();
						}
					});
				}
			}
		};
	}
}
